#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <sqlite3.h>
#import <QuartzCore/QuartzCore.h>
#import <LockInfo/Plugin.h>
#import <MapKit/MapKit.h>

#import "FWCommon.h"
#import "FWPlugin.h"
#import "FWDetailListView.h"
#import "FWRoundedLabel.h"
#import "FWPopupView.h"
#import "FWColorIconView.h"
#import "FWDetailListSection.h"
#import "FWDetailListEntry.h"


static CGFloat iPhoneCellInset = 10;
static CGFloat iPhoneCellBorder = 10;

static CGFloat iPadCellInset = 45;
static CGFloat iPadCellBorder = 10;

static CGFloat iconPadding = 8;

static CGFloat const kNavButtonWidth = 44.0;


static CGSize const iPhoneProgressBarSize = CGSizeMake(100, 9);
static CGSize const iPadProgressBarSize = CGSizeMake(300, 9);
static CGSize const ButtonSize = CGSizeMake(80, 37);
static CGSize const iPadSwitchSize = CGSizeMake(100, 27);
static CGSize const iPhoneSwitchSize = CGSizeMake(100, 27);

static CGFloat const NavigationAnimationDuration = 0.4;
static NSString* const NavigationTransitionType = kCATransitionPush;
static NSString* const NavigationTimingFunctionName = kCAMediaTimingFunctionDefault;


@implementation FWDetailListView

@synthesize delegate, index, application, list, textView, listView, navControl, caption, navBar, sections, linkStack, reloadViewOffset, reloadViewAnimated, detailTextColor, accessoryView, listKey;


// ******************************************************************************************
#pragma mark -
#pragma mark Initialisation and object management

- (id)initWithFrame:(CGRect)r list:(NSArray*)l index:(NSInteger)i caption:(NSString*)capt application:(FWApplication*)app delegate:(id<FWDetailListViewDelegate>)d
{
    if ((self = [super initWithFrame:r])) {
        self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        self.autoresizesSubviews = YES;
        
        self.list = l;
        self.delegate = d;
        self.application = app;
        self.caption = capt;
        [self buildPreview:r];
        self.index = i;
        
        self.reloadViewOffset = 0;
        self.reloadViewAnimated = YES;
        
        self.linkStack = [[[NSMutableArray alloc] init] autorelease];
    }

    return self;
}

- (id)initWithFrame:(CGRect)r list:(NSArray*)l index:(NSInteger)i application:(FWApplication*)app delegate:(id<FWDetailListViewDelegate>)d
{
    if ((self = [self initWithFrame:r list:l index:i caption:@"" application:app delegate:d])) {
    }
    
    return self;
}

- (void)dealloc
{
    [delegate release];
    [list release];
    [listKey release];
    [listView release];
    [textView release];
    [application release];
    [navControl release];
    [sections release];
    [navBar release];
    [caption release];
    [linkStack release];
    [detailTextColor release];
    [accessoryView release];
    
    [super dealloc];
}


// ******************************************************************************************
#pragma mark -
#pragma mark Attribute access and support

- (void)setCaption:(NSString*)newCaption
{
    NSBundle* bundle = [NSBundle bundleForClass:[self class]];
    [caption release];
    caption = localize(bundle, newCaption);
    [caption retain];
    
    if (self.navBar) {
        UINavigationItem* nav = self.navBar.topItem;
        nav.title = caption;
    }
}

- (void)setIndex:(NSInteger)i
{
    // Protect against invalid select
    if ((i < 0) || (i >= self.list.count))
        return;
    
    lastIndexWasEarlier = index < i;
    index = i;
    
    if ([self.list count] > 1) {
        [self.navControl setEnabled:(i > 0) forSegmentAtIndex:0];
        [self.navControl setEnabled:(i < ((int)[self.list count] - 1)) forSegmentAtIndex:1];
    }
    
    if ((linkStack.count == 0) && [delegate respondsToSelector:@selector(willShowBasePage:)])
        [delegate willShowBasePage:[list objectAtIndex:i]];
    
    self.sections = [delegate dataForDetailListPage:[list objectAtIndex:i]
                                                key:self.listKey
                                        application:self.application];
    
    if ([linkStack count] == 0) {
        NSRange range = [caption rangeOfString:@"%i"];
        if (range.location != NSNotFound) {
            UINavigationItem* nav = self.navBar.topItem;
            nav.title = [NSString stringWithFormat:caption, i+1, [self.list count]];
        }
    }
    
    [self reloadData:reloadViewAnimated];
    
    // See if any elements in the list are draggable
    BOOL edit = NO;
    for (int section = 0; section < [self.sections count]; ++section) {
        NSArray* items = [self tableView:self.listView sectionContentsForIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]];
        for (FWDetailListEntry* item in items) {
            if (item.isDraggable) {
                edit = YES;
                break;
            }
        }
        
        if (edit)
            break;
    }

    [self.listView setEditing:edit animated:NO];
}


// ******************************************************************************************
#pragma mark -
#pragma mark Main window layout

- (void)buildPreview:(CGRect)rect
{
    NSBundle* bundle = [NSBundle bundleForClass:[self class]];
    
    UIBarButtonItem* doneBtn = [[[UIBarButtonItem alloc]
                                 initWithTitle:localize(bundle, @"Detail/Button/Done")
                                 style:UIBarButtonItemStyleBordered 
                                 target:self
                                 action:@selector(doneClicked)] autorelease];
    
    UIBarButtonItem* cancelBtn = [[[UIBarButtonItem alloc]
                                   initWithTitle:localize(bundle, @"Detail/Button/Cancel")
                                   style:UIBarButtonItemStyleBordered 
                                   target:self
                                   action:@selector(cancelClicked)] autorelease];
    
    UIBarButtonItem* selectBtn = [[[UIBarButtonItem alloc]
                                   initWithTitle:localize(bundle, @"Detail/Button/Select")
                                   style:UIBarButtonItemStyleBordered 
                                   target:self
                                   action:@selector(selectClicked)] autorelease];
    
    UIImage* btnUp = [UIImage imageWithContentsOfFile:@"/Applications/MobileMail.app/arrowup.png"];								  
    UIImage* btnDown = [UIImage imageWithContentsOfFile:@"/Applications/MobileMail.app/arrowdown.png"];								  
    self.navControl = [[[UISegmentedControl alloc]
                        initWithItems:
                        [NSArray arrayWithObjects:
                         btnUp,
                         btnDown,
                         nil]] autorelease];
    
    if ((btnUp == nil) || (btnDown == nil))
        self.navControl = [[[UISegmentedControl alloc]
                            initWithItems:
                            [NSArray arrayWithObjects:
                             //                             localize(bundle, @"Detail/Button/Up"),
                             //                             localize(bundle, @"Detail/Button/Down"),
                             @"U", @"D",
                             nil]] autorelease];
    
    self.navControl.segmentedControlStyle = UISegmentedControlStyleBar;
    self.navControl.momentary = YES;
    [self.navControl setWidth:kNavButtonWidth forSegmentAtIndex:0];
    [self.navControl setWidth:kNavButtonWidth forSegmentAtIndex:1];
    
    [self.navControl addTarget:self
                        action:@selector(navigateClicked:)
              forControlEvents:UIControlEventValueChanged];
    
    UIBarButtonItem* navBtn = [[[UIBarButtonItem alloc] initWithCustomView:self.navControl] autorelease];
    
    const CGFloat toolbarHeight = 44;
    CGRect navBarFrame = CGRectMake(0, 0, rect.size.width, toolbarHeight);
    CGRect taskBarFrame = CGRectMake(0, rect.size.height - toolbarHeight, rect.size.width, toolbarHeight);
    CGRect scrollFrame = CGRectMake(0, navBarFrame.size.height, rect.size.width, rect.size.height - taskBarFrame.size.height - navBarFrame.size.height);
    
    UINavigationBar* nav = [[[UINavigationBar alloc] initWithFrame:navBarFrame] autorelease];
    nav.delegate = self;
    nav.autoresizingMask = UIViewAutoresizingFlexibleWidth;
    nav.barStyle = UIBarStyleBlackOpaque;
    self.navBar = nav;
    
    UINavigationItem* navItem = [[[UINavigationItem alloc] initWithTitle:self.caption] autorelease];
    
    if ([delegate respondsToSelector:@selector(cancelClicked:sections:)]) {
        navItem.leftBarButtonItem = cancelBtn;
        
        if ([delegate respondsToSelector:@selector(doneClicked:sections:)])
            navItem.rightBarButtonItem = doneBtn;
        else if ([delegate respondsToSelector:@selector(selectClicked:sections:)])
            navItem.rightBarButtonItem = selectBtn;
    }
    else if ([delegate respondsToSelector:@selector(doneClicked:sections:)])
        navItem.leftBarButtonItem = doneBtn;
    
    if ([self.list count] > 1)
        navItem.rightBarButtonItem = navBtn;
    
    [nav setItems:[NSArray arrayWithObjects:navItem, nil] animated:NO];
    [self addSubview:nav];
    
    scrollFrame.size.height += taskBarFrame.size.height;
    
    UITableView* table = [[UITableView alloc] initWithFrame:scrollFrame style:UITableViewStyleGrouped];
    table.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    table.autoresizesSubviews = YES;
    table.dataSource = self;
    table.delegate = self;
    table.allowsSelection = YES;
    
    if ([[UIColor class] respondsToSelector:@selector(underPageBackgroundColor)])
        table.backgroundColor = [UIColor underPageBackgroundColor];
    
    [self addSubview:table];
    self.listView = table;
    [listView release];

    UITextView* text = [[UITextView alloc] initWithFrame:scrollFrame];
    text.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    text.autoresizesSubviews = YES;
    text.font = [UIFont systemFontOfSize:[UIFont systemFontSize]];
    text.hidden = YES;
    text.editable = NO;
    text.dataDetectorTypes = UIDataDetectorTypeNone;
    self.detailTextColor = [UIColor blueColor];
    
    [self addSubview:text];
    self.textView = text;
    [textView release];
}

- (void)setFrame:(CGRect)newFrame
{
    BOOL shouldReload = !CGRectEqualToRect(self.frame, newFrame);
    [super setFrame:newFrame];

    // If the size of the frame has changed then reload the contents which
    // will ensure any contents will be re-laid out
    if (shouldReload)
        [self.listView reloadData];
}

- (void)showListView:(BOOL)show
{
    //self.listView.hidden = !show;
    self.textView.hidden = show;
}

- (void)showAccessoryView:(UIView*)accessory
{
    //self.listView.hidden = !show;
    self.textView.hidden = YES;
    
    if (self.accessoryView)
        [self.accessoryView removeFromSuperview];
    self.accessoryView = accessory;
    
    if (accessory) {
        accessory.frame = self.textView.frame;
        [self addSubview:accessory];
    }
}


// ******************************************************************************************
#pragma mark -
#pragma mark Tableview section information

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return [self.sections count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    FWDetailListSection* sect = [self.sections objectAtIndex:section];
    return sect.entries.count;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    FWDetailListSection* sect = [self.sections objectAtIndex:section];
    return sect.title;
}

- (CGFloat)tableView:(UITableView*)tableView heightForHeaderInSection:(NSInteger)section
{
    FWDetailListSection* sect = [self.sections objectAtIndex:section];
    
    if (sect.title)
        return [UIFont boldSystemFontOfSize:[UIFont labelFontSize]].leading + 6;
    else {
        if (section == 0)
            return 4.0;
        else
            return 1.0;
    }
}

- (CGFloat)tableView:(UITableView*)tableView heightForFooterInSection:(NSInteger)section
{
    return 2.0;
}

- (UIView*)tableView:(UITableView*)tableView viewForHeaderInSection:(NSInteger)section
{
    FWDetailListSection* sect = [self.sections objectAtIndex:section];
    
    if (sect.title)
        return nil;
    else
        return [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)] autorelease];
}

- (UIView*)tableView:(UITableView*)tableView viewForFooterInSection:(NSInteger)section
{
    return [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)] autorelease];
}


// ******************************************************************************************
#pragma mark -
#pragma mark Tableview element layout

- (CGSize)tableView:(UITableView *)tableView constraintForTextFieldWithIcon:(UIImage*)icon progress:(BOOL)progress swtch:(BOOL)swtch selected:(BOOL)selected link:(BOOL)link button:(BOOL)button accessory:(UIView*)accessory
{
    BOOL iPad = (UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPhone);
    CGFloat cellInset = iPad ? iPadCellInset : iPhoneCellInset;
    CGFloat cellBorder = iPad ? iPadCellBorder : iPhoneCellBorder;

    CGFloat width = tableView.frame.size.width - 2 * (cellInset + cellBorder);
    CGFloat iconWidth = (icon != nil) ? (icon.size.width + iconPadding): 0;
    CGSize constraint = CGSizeMake(width - iconWidth, MAXFLOAT);
    
    if (progress)
        constraint.width -= iPad ? iPadProgressBarSize.width : iPhoneProgressBarSize.width;
    else if (swtch) 
        constraint.width -= iPad ? iPadSwitchSize.width : iPhoneSwitchSize.width;
    else if (selected) 
        constraint.width -= 32;
    else if (link)
        constraint.width -= (9 + iconPadding);
    else if (button)
        constraint.width -= ButtonSize.width;

    if (accessory)
        constraint.width -= (accessory.frame.size.width + iconPadding);
    
    return constraint;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    BOOL iPad = (UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPhone);
    CGFloat cellInset = iPad ? iPadCellInset : iPhoneCellInset;
    CGFloat cellBorder = iPad ? iPadCellBorder : iPhoneCellBorder;
    CGFloat width = tableView.frame.size.width - 2 * (cellInset + cellBorder);

    FWDetailListSection* section = [self.sections objectAtIndex:indexPath.section];
    FWDetailListEntry* elem = [section.entries objectAtIndex:indexPath.row];
    
    if (elem.isProgress || elem.isSwitcher || elem.isButton || !elem.value || ([[elem.value stringValue] length] == 0)) {
        // If we have a message then use it as the basis for the title size
        NSString* title = elem.title;
        if (elem.message)
            title = elem.message;
        
        UIFont* font = elem.isNotBold ? [UIFont systemFontOfSize:elem.size] : [UIFont boldSystemFontOfSize:elem.size];

        CGSize constraintSize = [self tableView:tableView 
                 constraintForTextFieldWithIcon:elem.icon
                                       progress:elem.isProgress
                                          swtch:elem.isSwitcher
                                       selected:NO
                                           link:elem.isLink
                                         button:elem.isButton
                                      accessory:elem.accessoryView];

        CGSize titleSize = [title sizeWithFont:font
                             constrainedToSize:constraintSize
                                 lineBreakMode:UILineBreakModeWordWrap];
        
        CGFloat height = titleSize.height + cellBorder * 2;
        
        if (elem.isButton && (height < (ButtonSize.height + cellBorder * 2)))
            height = ButtonSize.height + cellBorder * 2;

        return height;
    }
    else {
        CGSize titleSize = [elem.title sizeWithFont:[UIFont boldSystemFontOfSize:[UIFont labelFontSize]]];
        CGSize constraint = CGSizeMake(width - titleSize.width - 6, MAXFLOAT);
        
        if (elem.isLink)
            constraint.width -= (9 + cellBorder);
        if (elem.accessoryView)
            constraint.width -= (elem.accessoryView.frame.size.width + cellBorder);
        
        CGSize detailSize = [[elem.value stringValue] sizeWithFont:[UIFont systemFontOfSize:elem.size] 
                                                 constrainedToSize:constraint
                                                     lineBreakMode:UILineBreakModeWordWrap];
        
        if (titleSize.height > detailSize.height)
            return titleSize.height + cellBorder * 2;
        else
            return detailSize.height + cellBorder * 2;
    }
}


- (FWDetailListSection*)tableView:(UITableView*)tableView sectionInfoForIndexPath:(NSIndexPath*)indexPath
{
    return [self.sections objectAtIndex:indexPath.section];
}

- (NSArray*)tableView:(UITableView*)tableView sectionContentsForIndexPath:(NSIndexPath*)indexPath
{
    FWDetailListSection* section = [self.sections objectAtIndex:indexPath.section];
    return section.entries;
}

- (FWDetailListEntry*)tableView:(UITableView*)tableView elementContentsForIndexPath:(NSIndexPath*)indexPath
{
    FWDetailListSection* section = [self.sections objectAtIndex:indexPath.section];
    return [section.entries objectAtIndex:indexPath.row];
}


// ******************************************************************************************
#pragma mark -
#pragma mark Tableview element access

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    BOOL iPad = (UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPhone);

    FWDetailListSection* section = [self.sections objectAtIndex:indexPath.section];
    FWDetailListEntry* elem = [section.entries objectAtIndex:indexPath.row];
        
    static NSString *MyIdentifier = @"PITodoPreview";
    static NSInteger messageTag = 9998;
    static NSInteger roundedTag = 9999;
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
                                       reuseIdentifier:MyIdentifier] autorelease];
        
        UILabel* message = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
        message.font = [UIFont boldSystemFontOfSize:[UIFont labelFontSize]];
        message.tag = messageTag;
        message.textAlignment = UITextAlignmentCenter;
        message.backgroundColor = [UIColor clearColor];
        message.hidden = YES;
        [cell.contentView addSubview:message];
        
        FWRoundedLabel* label = [[[FWRoundedLabel alloc] initWithFrame:CGRectZero] autorelease];
        label.tag = roundedTag;
        [cell.contentView addSubview:label];
    }

    cell.selectionStyle = UITableViewCellSelectionStyleNone;
    cell.accessoryType = UITableViewCellAccessoryNone;
    cell.accessoryView = elem.accessoryView;
    cell.backgroundColor = elem.backgroundColor;
    [[cell.contentView viewWithTag:messageTag] setHidden:YES];
    
    if (elem.isProgress) {
        UIProgressView *progressBar = [[[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault] autorelease];
        progressBar.progress = [(NSNumber*)elem.value floatValue];
        CGRect size = progressBar.frame;
        size.size.width = iPad ? iPadProgressBarSize.width : iPhoneProgressBarSize.width;
        progressBar.frame = size;
        cell.accessoryView = progressBar;
    }
    else if (elem.switcher) {
        UISwitch *switcher = [[[UISwitch alloc] initWithFrame:CGRectZero] autorelease];
        switcher.on = [(NSNumber*)elem.value boolValue];
        switcher.tag = indexPath.section * 100 + indexPath.row;
        [switcher addTarget:self action:@selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
        cell.accessoryView = switcher;
        
        if ([switcher respondsToSelector:@selector(setAlternateColors:)])
            [switcher setAlternateColors:YES];
    }
    else if (elem.isButton) {
        Class cls = objc_getClass("UIGlassButton");
        UIButton *btn = [[[cls alloc] initWithFrame:CGRectMake(0, 0, ButtonSize.width, ButtonSize.height)] autorelease];
        btn.showsTouchWhenHighlighted = YES;
        [btn addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];

        [btn setTitle:elem.value forState:UIControlStateNormal];
        btn.tag = indexPath.section * 100 + indexPath.row;
        cell.accessoryView = btn;
    }
    else if (elem.isSelected && [(NSNumber*)elem.value boolValue]) {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
    }        
    else if (elem.isLink || elem.isBackLink) {
        cell.selectionStyle = UITableViewCellSelectionStyleBlue;
        NSBundle* bundle = [NSBundle bundleForClass:[self class]];
        FWColorIconView* icon = [[[FWColorIconView alloc] initWithFrame:CGRectMake(0, 0, 9, 14)] autorelease];
        icon.color = [UIColor darkGrayColor];
        icon.icon = [bundle pathForResource:elem.link ? @"arrow-right" : @"arrow-left" ofType:@"png"];
        cell.accessoryView = icon;
    }        
    else if (NSString* message = elem.message) {
        UILabel* label = (UILabel*)[cell.contentView viewWithTag:messageTag];
        CGFloat cellInset = iPad ? iPadCellInset : iPhoneCellInset;
        CGFloat cellBorder = iPad ? iPadCellBorder : iPhoneCellBorder;
        
        label.font = [UIFont boldSystemFontOfSize:elem.size];
        CGFloat width = tableView.frame.size.width - 2 * (cellInset + cellBorder);
        CGFloat height = [label.font leading]; 
        
        label.frame = CGRectMake(cellBorder, cellBorder, width, height);
        label.hidden = NO;
        label.text = message;
        label.textColor = elem.color;      
    }
    
    if (!elem.isNotBold)
        cell.textLabel.font = [UIFont boldSystemFontOfSize:elem.size];
    else
        cell.textLabel.font = [UIFont systemFontOfSize:elem.size];
    
    cell.textLabel.text = elem.title;
    cell.detailTextLabel.font = [UIFont systemFontOfSize:elem.size];
    cell.imageView.image = elem.icon;

    if (elem.isSwitcher || elem.isSelected || elem.isButton)
        cell.detailTextLabel.text = nil;
#ifndef DEVELOPMENT_MODE
    else if (elem.isProgress)
        cell.detailTextLabel.text = nil;
#endif
    else
        cell.detailTextLabel.text = [elem.value stringValue];
    
    if (!elem.isButton && elem.value && ([[elem.value stringValue] length] > 0)) {
        if (elem.lines < 0) {
            cell.textLabel.numberOfLines = 1;
            cell.detailTextLabel.numberOfLines = 0;
        }
        else {
            cell.textLabel.numberOfLines = elem.lines;
            cell.detailTextLabel.numberOfLines = elem.lines;
        }			

        cell.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap;
        cell.textLabel.textColor = elem.color;
        self.detailTextColor = cell.detailTextLabel.textColor;
    }
    else {
        if (elem.lines < 0) {
            cell.textLabel.numberOfLines = 0;
            cell.detailTextLabel.numberOfLines = 1;
        }
        else {
            cell.textLabel.numberOfLines = elem.lines;
            cell.detailTextLabel.numberOfLines = elem.lines;
        }			

        cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
        
        if (elem.isNotBold) {
            cell.textLabel.textColor = cell.detailTextLabel.textColor;
            self.detailTextColor = cell.detailTextLabel.textColor;
        }
        else {
            cell.textLabel.textColor = elem.color;
        }
    }
    
    if (elem.colorAccessory && [elem.accessoryView respondsToSelector:@selector(setColor:)])
        [elem.accessoryView setColor:(CGColorRef)self.detailTextColor];
    
    FWRoundedLabel* roundLabel = (FWRoundedLabel*)[cell.contentView viewWithTag:roundedTag];
    if (elem.info) {
        roundLabel.hidden = false;

        roundLabel.label.font = [UIFont systemFontOfSize:elem.size - 2];
        roundLabel.label.text = elem.info;
        //roundLabel.label.textColor = [UIColor whiteColor];
        roundLabel.label.textAlignment = UITextAlignmentCenter;
        //roundLabel.label.shadowOffset = CGSizeMake(0.0, 1.0);
        //roundLabel.label.shadowColor = [UIColor lightGrayColor];
        //roundLabel.roundedColor = [UIColor darkGrayColor];
        roundLabel.textColor = [UIColor blackColor];
        roundLabel.roundedColor = [UIColor orangeColor];

        CGSize labelSize = [roundLabel sizeForHeight:roundLabel.label.font.leading];
        //labelSize.width *= 0.75;
        CGFloat cellHeight = [self tableView:tableView heightForRowAtIndexPath:indexPath];
        CGFloat cellBorder = iPad ? iPadCellBorder : iPhoneCellBorder;
        CGFloat indent = (cellHeight - labelSize.height) / 2.0;

        CGSize constraintSize = [self tableView:tableView 
                 constraintForTextFieldWithIcon:nil
                                       progress:elem.isProgress
                                          swtch:elem.isSwitcher
                                       selected:elem.isSelected
                                           link:elem.isLink || elem.isBackLink
                                         button:elem.isButton
                                      accessory:elem.accessoryView];
        
        roundLabel.frame = CGRectMake(cellBorder + constraintSize.width - labelSize.width, 
                                      indent,
                                      labelSize.width,
                                      labelSize.height);
        
        [cell.contentView bringSubviewToFront:roundLabel];
    }
    else
        roundLabel.hidden = true;
    
    return cell;
}


// ******************************************************************************************
#pragma mark -
#pragma mark Animation and eye-candy

- (void)playReloadAnimation
{
    CATransition *animation = [CATransition animation];
    [animation setType:@"pageUnCurl"];
    
    if (lastIndexWasEarlier)
        [animation setSubtype:kCATransitionFromLeft];
    else
        [animation setSubtype:kCATransitionFromRight];
    
    [animation setDuration:0.35];
    [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
    [animation setFillMode: @"extended"];

    [[self.listView layer] addAnimation:animation forKey:@"reloadAnimation"];
}

- (void)reloadDataWithObject:(NSNumber*)animated
{
    [self.listView reloadData];
    
    if ([animated boolValue])
        [self playReloadAnimation];
}

- (void)reloadData:(BOOL)animated
{
    [self.listView reloadData];
    self.listView.contentOffset = CGPointMake(0, reloadViewOffset);
    
    if (animated)
        [self playReloadAnimation];
}


// ******************************************************************************************
#pragma mark -
#pragma mark Sub/super table selection hanndling

static NSString* const kStackList = @"list";
static NSString* const kStackCaption = @"caption";
static NSString* const kStackIndex = @"index";
static NSString* const kStackOffset = @"offset";
static NSString* const kStackKey = @"key";

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    FWDetailListSection* section = [self.sections objectAtIndex:indexPath.section];
    FWDetailListEntry* elem = [[section.entries objectAtIndex:indexPath.row] retain];
    
    if (elem.isLink) {
        DebugLog(@"LI:PICommon: row selected");
        
        [tableView deselectRowAtIndexPath:indexPath animated:NO];

        // Record where we are
        NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
                              self.list, kStackList,
                              [NSNumber numberWithInt:self.index], kStackIndex,
                              [NSNumber numberWithFloat:self.listView.contentOffset.y], kStackOffset,
                              self.navBar.topItem.title, kStackCaption,
                              self.listKey ? self.listKey : kStackKey, kStackKey,
                              nil];
        [linkStack addObject:dict];
        NSString* popupMessage = nil;
        if ([delegate respondsToSelector:@selector(listPagePopupForKey:currentPage:displayDepth:application:)]) {
            popupMessage = [delegate listPagePopupForKey:elem.key
                                             currentPage:[self.list objectAtIndex:self.index]
                                            displayDepth:[self.linkStack count]
                                             application:application];
        }
        
        NSString* text = nil;
        if ([delegate respondsToSelector:@selector(listPageTextForKey:currentPage:displayDepth:application:)])
            text = [delegate listPageTextForKey:elem.key
                                    currentPage:[self.list objectAtIndex:self.index]
                                   displayDepth:[self.linkStack count]
                                    application:application];

        UIView* accessory = nil;
        if ([delegate respondsToSelector:@selector(listPageViewForKey:currentPage:displayDepth:rect:application:)])
            accessory = [delegate listPageViewForKey:elem.key
                                         currentPage:[self.list objectAtIndex:self.index]
                                        displayDepth:[self.linkStack count]
                                                rect:self.textView.frame
                                         application:application];

        if (!text && !accessory) {
            // Set up data for the new view
            self.reloadViewOffset = 0;
            self.reloadViewAnimated = NO;
            self.listKey = elem.key;
            self.list = [delegate listPageInfoForKey:elem.key
                                         currentPage:[self.list objectAtIndex:self.index]
                                        displayDepth:[self.linkStack count]
                                         application:application];
            self.index = 0;
        }
        
        NSString* newCaption = @"";
        if ([delegate respondsToSelector:@selector(captionForListPage:forKey:displayDepth:application:)])
            newCaption = [delegate captionForListPage:self.list
                                               forKey:elem.key
                                         displayDepth:[self.linkStack count]
                                          application:application];
            
        UINavigationItem* navItem = [[[UINavigationItem alloc] initWithTitle:newCaption] autorelease];

        NSBundle* bundle = [NSBundle bundleForClass:[self class]];
        UIBarButtonItem* doneBtn = [[[UIBarButtonItem alloc]
                                     initWithTitle:localize(bundle, @"Detail/Button/Done")
                                     style:UIBarButtonItemStyleBordered 
                                     target:self
                                     action:@selector(doneClicked)] autorelease];
        navItem.rightBarButtonItem = doneBtn;
        
        [self.navBar pushNavigationItem:navItem animated:YES];

        CATransition *animation = [CATransition animation];
        [animation setType:NavigationTransitionType];
        [animation setSubtype:kCATransitionFromRight];
        [animation setDuration:NavigationAnimationDuration];
        [animation setTimingFunction:[CAMediaTimingFunction functionWithName:NavigationTimingFunctionName]];

        if (text) {
            self.textView.text = text;
            self.textView.textColor = self.detailTextColor;
            self.textView.contentOffset = CGPointMake(0, 0);
            [self showListView:NO];
            [[self.textView layer] addAnimation:animation forKey:NULL];
        }
        else if (accessory) {
            self.textView.hidden = YES;
            [self showAccessoryView:accessory];
            [[accessory layer] addAnimation:animation forKey:NULL];
        }
        else {
            [self showListView:YES];
            [[self.listView layer] addAnimation:animation forKey:NULL];
        }
        
        if (popupMessage)
            [FWPopupView displayPopupInstruction:popupMessage forView:self];
    }
    else if (elem.isBackLink) {
        DebugLog(@"LI:PICommon: backlink row selected");
        
        [self.navBar popNavigationItemAnimated:YES];
        [self showListView:YES];
    }
    
    [elem release];
}

- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
    if ([linkStack count] == 0)
        return NO;
    
    NSDictionary* info = [[[linkStack lastObject] retain] autorelease];
    [linkStack removeLastObject];
    NSArray* arrayValue = [info objectForKey:kStackList];
    NSNumber* indexValue = [info objectForKey:kStackIndex];
    NSNumber* offsetValue = [info objectForKey:kStackOffset];
    NSString* captionValue = [info objectForKey:kStackCaption];
    NSObject* keyValue = [info objectForKey:kStackKey];
    if (keyValue == kStackKey)
        keyValue = nil;
    
    self.list = arrayValue;
//    self.caption = captionValue;
    self.reloadViewOffset = [offsetValue floatValue];
    self.reloadViewAnimated = NO;
    self.listKey = keyValue;
    self.index = [indexValue intValue];
    
    CATransition *animation = [CATransition animation];
    [animation setType:NavigationTransitionType];
    [animation setSubtype:kCATransitionFromLeft];
    [animation setDuration:NavigationAnimationDuration];
    [animation setTimingFunction:[CAMediaTimingFunction functionWithName:NavigationTimingFunctionName]];
    
    [[self.listView layer] addAnimation:animation forKey:NULL];
    [self showListView:YES];
    [self showAccessoryView:nil];
    
    return YES;
}


// ******************************************************************************************
#pragma mark -
#pragma mark Command button callbacks

- (void)cancelClicked
{
    if ([delegate cancelClicked:self.application sections:self.sections])
        [self.application.fwPlugin.liPlugin dismissPreview];
}

- (void)doneClicked
{
    if ([delegate doneClicked:self.application sections:self.sections])
        [self.application.fwPlugin.liPlugin dismissPreview];
}

- (void)selectClicked
{
    if ([delegate selectClicked:self.application sections:self.sections])
        [self.application.fwPlugin.liPlugin dismissPreview];
}

- (void)buttonClicked:(id)sender
{
    UIButton* sw = (UIButton*)sender;
    NSInteger section = (int)(sw.tag / 100);
    NSInteger row = (sw.tag % 100);
    
    FWDetailListEntry* entry = [self tableView:self.listView
                   elementContentsForIndexPath:[NSIndexPath indexPathForRow:row inSection:section]];

    if ([delegate buttonClicked:self.application sections:self.sections button:entry])
        [self.application.fwPlugin.liPlugin dismissPreview];
}

- (void)switchChanged:(id)sender
{
    UISwitch* sw = (UISwitch*)sender;
    NSInteger section = (int)(sw.tag / 100);
    NSInteger row = (sw.tag % 100);

    FWDetailListSection* sect = [self.sections objectAtIndex:section];
    FWDetailListEntry* element = [sect.entries objectAtIndex:row];
    
    element.value = [NSNumber numberWithBool:sw.on];
    
    if ([delegate respondsToSelector:@selector(switchChanged:sections:)])
        [delegate switchChanged:self.application sections:self.sections];
}

- (void)navigateClicked:(id)sender
{
    UISegmentedControl *control = (UISegmentedControl *)sender;
    self.reloadViewOffset = 0;
    self.reloadViewAnimated = YES;

    if ([control selectedSegmentIndex] == 0)
        self.index = self.index - 1;
    else
        self.index = self.index + 1;
}


// ******************************************************************************************
#pragma mark -
#pragma mark Table row reordering support

- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Don't allow emptying a section
    NSArray* elements = [self tableView:tableView sectionContentsForIndexPath:indexPath];
    if ([elements count] == 1)
        return UITableViewCellEditingStyleNone;

    if ([delegate respondsToSelector:@selector(allowDelete:indexPath:)]) {
        FWDetailListEntry* element = [elements objectAtIndex:indexPath.row];
        if ([delegate allowDelete:element indexPath:indexPath])
            return UITableViewCellEditingStyleDelete;
    }

    return UITableViewCellEditingStyleNone;
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSMutableArray* items = (NSMutableArray*)[self tableView:tableView sectionContentsForIndexPath:indexPath];
    FWDetailListEntry* item = [[items objectAtIndex:indexPath.row] retain];

    [items removeObjectAtIndex:indexPath.row];

    if ([delegate respondsToSelector:@selector(deletedEntry:sections:indexPath:)])
        [delegate deletedEntry:item sections:sections indexPath:indexPath];
    
    if ([delegate respondsToSelector:@selector(tableReordered:)])
        [delegate performSelector:@selector(tableReordered:) withObject:self.sections afterDelay:0];

    // refresh the table view
    [self performSelector:@selector(reloadDataWithObject:) withObject:[NSNumber numberWithBool:YES] afterDelay:0];
    [item release];
}

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSArray* elements = [self tableView:tableView sectionContentsForIndexPath:indexPath];
    
    // Don't allow emptying a section
    if ([elements count] == 1)
        return NO;
    
    // Only allow drag if the item supports it
    FWDetailListEntry* element = [elements objectAtIndex:indexPath.row];
    return element.isDraggable;
}

- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSArray* elements = [self tableView:tableView sectionContentsForIndexPath:indexPath];

    // Don't allow emptying a section
    if ([elements count] == 1)
        return NO;

    // Only allow drag if the item supports it
    FWDetailListEntry* element = [elements objectAtIndex:indexPath.row];
    return element.isDraggable;
}

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
    NSMutableArray* sourceItems = (NSMutableArray*)[self tableView:tableView sectionContentsForIndexPath:fromIndexPath];
    NSMutableArray* destItems = (NSMutableArray*)[self tableView:tableView sectionContentsForIndexPath:toIndexPath];

    FWDetailListEntry* item = [[sourceItems objectAtIndex:fromIndexPath.row] retain];
    [sourceItems removeObjectAtIndex:fromIndexPath.row];
    [destItems insertObject:item atIndex:toIndexPath.row];
    
    if ([delegate respondsToSelector:@selector(movedEntry:sections:fromIndexPath:toIndexPath:)])
        [delegate movedEntry:item sections:self.sections fromIndexPath:fromIndexPath toIndexPath:toIndexPath];

    if ([delegate respondsToSelector:@selector(tableReordered:)])
        [delegate performSelector:@selector(tableReordered:) withObject:self.sections afterDelay:0];

    if ([delegate respondsToSelector:@selector(shouldReloadWhenTableReordered:)]) {
        BOOL reload = [delegate shouldReloadWhenTableReordered:self.sections];
        if (reload)
            [self performSelector:@selector(reloadDataWithObject:) withObject:[NSNumber numberWithBool:YES] afterDelay:0];
    }

    [item release];
}

- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
    return NO;
}

- (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath
{
    NSArray* sourceItems = [self tableView:tableView sectionContentsForIndexPath:sourceIndexPath];
    FWDetailListSection* destSection = [self tableView:tableView sectionInfoForIndexPath:proposedDestinationIndexPath];
    
    // Does the destination section allow drags?
    if (!destSection.isDraggable)
        return sourceIndexPath;

    // Will we empty the source?
    if ([sourceItems count] == 1)
        return sourceIndexPath;
    
    if ([delegate respondsToSelector:@selector(targetIndexForMove:toSection:proposedIndexPath:)]) {
        FWDetailListEntry* entry = [sourceItems objectAtIndex:sourceIndexPath.row];
        NSInteger newIndex = [delegate targetIndexForMove:entry toSection:destSection proposedIndexPath:proposedDestinationIndexPath];
        proposedDestinationIndexPath = [NSIndexPath indexPathForRow:newIndex inSection:proposedDestinationIndexPath.section];
    }
    
    return proposedDestinationIndexPath;
}

@end
